package service

import (
	"context"
	"encoding/json"
	"fmt"
	"user-center/src/constant"
	"user-center/src/db"
	"user-center/src/model"
	"user-center/src/sdk/sandbox"

	"github.com/go-redis/redis/v8"
	"github.com/gorilla/websocket"
	log "github.com/sirupsen/logrus"
)

//下面使用的是实时消息队列
//目前概念是，所有人改完代码之后直接覆盖，这个方法也可以使用redis kv
//但是为来改进是哪里改动传送哪里，所以需要消息队列

func parseMessage(conn *websocket.Conn , v any) error {
	for {
		_, data, err := conn.ReadMessage()
		if err != nil {
			return err
		}	
		if string(data) != "ping" {
			return json.Unmarshal(data , v)
		}
		if err = conn.WriteMessage(websocket.TextMessage, []byte("pong")); err != nil {
			log.Println("Error sending ping:", err)
			return err
		}
	}
}

// 该函数只用于读取订阅消息数据并转发
func readRedisPubMessage(ctx context.Context, sub *redis.PubSub , conn *websocket.Conn ) {
	for {
		msg, err := sub.ReceiveMessage(ctx)
		if err != nil {
			fmt.Printf("ReceiveMessage error: %v\n", err)
			break
		}
		fmt.Printf("Received message from redis publish: %s\n", msg.Payload)
		postback(conn , msg.Payload)
	}
	//pubsub.Unsubscribe(ctx , )
}

// opt == 1 订阅频道
// opt == 2 为发送消息
// opt == 3 为取消订阅
func redisPublish(ctx context.Context, opt int, Channel string, sendContent any, pb *redis.PubSub , conn *websocket.Conn) *redis.PubSub {
	if opt == 1 {
		//Subscribe 是精确匹配，只能接收订阅的精确通道的消息。
		//PSubscribe 支持模式匹配，可以接收匹配特定模式的所有通道的消息。
		pubsub := db.RedisClient.Subscribe(ctx, Channel)
		go readRedisPubMessage(ctx, pubsub , conn)
		return pubsub
	} else if opt == 2 {
		data , _  := json.Marshal(sendContent)
		db.RedisClient.Publish(ctx, Channel, string(data))
		log.Info("redis publisg message success")
	} else if opt == 3 {
		err := pb.Unsubscribe(ctx, Channel)
		if err != nil {
			log.Error("Unsubscribe error ", err)
			return nil
		}
	}
	return nil
}

//回发result
func postback(conn *websocket.Conn  , message string) {
	err := conn.WriteMessage(websocket.TextMessage , []byte(message))
	if err != nil {
		log.Error("write message error", err)
	}
}

func executeCode(code *model.Code) (result string){
	if code.Execute {
		switch code.Language {
		case constant.CPP:
			result = sandbox.ExecuteCode("c++", code.Code)
		case constant.PYTHON:
			result = sandbox.ExecuteCode("python", code.Code)
		default:
			result = "这是啥语言啊， 还没有支持呢"
		}
	}
	return
}

// DealWebsocketConn 循环
// 1 获取代码数据解析参数
// 2 发布频道/订阅频道
// 3 判断操作
// 3.1 修改操作，推送频道信息
// 3.1.1 定义接受频道信息【自动发送给前端】
// 3.2 执行操作，调用docker sdk 执行结果 推送频道信息
// 3.3 关闭操作 结束
// 3.4 异步更新数据库中代码
func CodeDealWebsocketConn(conn *websocket.Conn) {
	firstConn := false
	var pubsub *redis.PubSub
	ctx := context.Background()
	log.Info("code websocket connection " , conn.LocalAddr().Network())
	for {
		// 1 获取代码数据解析参数
		code := &model.Code{}
		err := parseMessage(conn , code)
		if err != nil {
			//关闭取消订阅
			break
		}
		// 2 订阅频道
		if !firstConn {
			pubsub = redisPublish(ctx, 1, code.GroupId , code, nil , conn)
			firstConn = true
		}
		//  3 处理数据
		if code.Update {
			redisPublish(ctx, 2, code.GroupId , code, nil , conn)
		}
		if code.Execute{
			code.Result = executeCode(code)
			//推送
			redisPublish(ctx, 2, code.GroupId , code, nil , conn)
		}
	}

	pubsub.Unsubscribe(ctx)
	//退订所有订阅
	// pubsub.Close()
}
